<template>
  <div class="scene">
    <canvas id="three"></canvas>
  </div>
</template>

<script setup>
import { onMounted } from 'vue'
import * as THREE from 'three'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' // 轨道控制器

/**
 * onMounted 钩子函数
 */
onMounted(() => {
    /**
     * 场景 scene
     */
    const scene = new THREE.Scene()
    scene.background = new THREE.Color('#00DCFF')

    /**
     * 相机 camera
     */
    const camera = new THREE.PerspectiveCamera(
      50, // 透视摄像机，垂直视野角度
      window.innerWidth / window.innerHeight, // 摄像机横纵比
      10, // 近端面
      12000 // 远端面
    )

    // 手动调整摄像机位置
    camera.position.x = 0
    camera.position.z = 600
    camera.position.y = 480

    /**
     * 渲染器 renderer
     */
    const canvas = document.querySelector('#three') // 将 canvas 绑定到元素上
    const renderer = new THREE.WebGLRenderer({
      canvas,
      antialias: true // 抗锯齿
    })

    /**
     * 导入 gltf 格式的模型
     */
    let mixer
    let carAction
    let action
    const gltfLoader = new GLTFLoader()
    gltfLoader.load('./static/models/scene3.gltf', (gltf) => {
      const model = gltf.scene
      scene.add(model)
      
      /**
       * 初始化动画
       */
      // 我们需要创建 AnimationMixer，它就像音乐播放器一样，帮助我们播放动画
      mixer = new THREE.AnimationMixer(gltf.scene)

      // 使用 clipAction 方法将动画信息添加到 mixer 中
      // action = mixer.clipAction(gltf.animations[0])
      // action.play() // 自动播放

      // 多个动画
      gltf.animations.forEach((item) => {
        mixer.clipAction(item).play()
      })

      // 同样的，如果想单独控制某个动画，给它
      // carAction = mixer.clipAction(gltf.animations[4])
      // carAction.play()
    })

    /**
     * 按下键盘
     */
    // document.addEventListener('keydown', function(e) {
    //     if(e.keyCode === 81) carAction.play()
    //     if(e.keyCode === 87) carAction.stop()
    //     if(e.keyCode === 65) carAction.paused = true // 暂停后只有重新设置 false 才能继续
    //     if(e.keyCode === 83) carAction.paused = false
    // })

    /**
     * 射线检测
     */
    function raycasterTest(e) {
        e.preventDefault()
        const { clientX, clientY } = e
        const dom = renderer.domElement
        // 拿到canvas画布到屏幕的距离
        const domRect = dom.getBoundingClientRect()
        // 计算标准设备坐标 - 归一化设备坐标
        const x = ((clientX - domRect.left) / dom.clientWidth) * 2 - 1
        const y = -((clientY - domRect.top) / dom.clientHeight) * 2 + 1
        const vector = new THREE.Vector3(x, y)
        // 转世界坐标
        const worldVector = vector.unproject(camera)
        console.log('世界坐标', worldVector)
        // 向量相减，并获取单位向量
        const ray = worldVector.sub(camera.position).normalize()
        // 射线投射对象， 第一个参数是射线原点 第二个参数是射线方向
        const raycaster = new THREE.Raycaster(camera.position, ray)
        raycaster.camera = camera
    
        //返回射线选中的对象 //第一个参数是检测的目标对象 第二个参数是目标对象的子元素
        const intersects= raycaster.intersectObjects(scene.children)
    
        if (intersects.length > 0) {
            console.log("捕获到对象", intersects);
            if(intersects[0] && intersects[0].object && intersects[0].object.name === '立方体1') {
                alert('点中了无人机')
            }
        } else {
            console.log("没捕获到对象");
        }
    }
    window.addEventListener('click', raycasterTest)

    /**
     * 轨道控制器
     */
    const controls = new OrbitControls(camera, renderer.domElement); // 传入 将要被控制的相机 / 用于事件监听的HTML元素
    controls.enableDamping = true // 启用惯性
    controls.maxPolarAngle = Math.PI * 0.5 // 相机旋转最大角度，不会超过地面

    /**
     * 创建地面
     */
    const plane = createPlane()
    scene.add(plane)
    
    function createPlane() {
      var textureLoader = new THREE.TextureLoader();
      var cubeMaterial = new THREE.MeshStandardMaterial({
        map: textureLoader.load("static/textures/cd.jpeg"),
      });
      cubeMaterial.map.wrapS = THREE.RepeatWrapping;
      cubeMaterial.map.wrapT = THREE.RepeatWrapping;
      cubeMaterial.map.repeat.set(8, 8)
      // 创建地平面并设置大小
      var planeGeometry = new THREE.PlaneGeometry(3000, 3000);
      var plane = new THREE.Mesh(planeGeometry, cubeMaterial);

      // 设置平面位置并旋转
      plane.rotation.x = -0.5 * Math.PI;
      plane.position.x = 0;
      plane.position.y = 6;
      plane.position.z = 0;
      return plane
    }
    
    /**
     * 循环渲染每一帧
     */
    const clock = new THREE.Clock() // 计帧器
    function animate() {
      renderer.render(scene, camera) // 刷新渲染器
      controls.update(); // 刷新轨道控制器

      /**
       * 帧动画相关
       */
      if (mixer) mixer.update(clock.getDelta())

      requestAnimationFrame(animate)
    }
    animate()

    /**
     *  添加光源
     */
    // 添加个平行光
    const dirLight = new THREE.DirectionalLight(0xffffff, 1);
    // 光源等位置
    dirLight.position.set(10, 10, 10);
    // 可以产生阴影
    dirLight.castShadow = true;
    dirLight.shadow.mapSize = new THREE.Vector2(1024, 1024);
    scene.add(dirLight);
    
    // 添加个半球光
    const hemLight = new THREE.HemisphereLight(0xffffff, 0xffffff, 0.6);
    hemLight.position.set(0, 100, 0);
    scene.add(hemLight);

    /**
     * 调整设备 物理像素分辨率与 CSS 像素分辨率的比值
     */
    function resizeRendererToDisplaySize(renderer) {
      const canvas = renderer.domElement;
      var width = window.innerWidth;
      var height = window.innerHeight;
      var canvasPixelWidth = canvas.width / window.devicePixelRatio;
      var canvasPixelHeight = canvas.height / window.devicePixelRatio;
    
      const needResize = canvasPixelWidth !== width || canvasPixelHeight !== height
      if (needResize) {
        renderer.setSize(width, height, false)
      }
      return needResize
    }
    
    if (resizeRendererToDisplaySize(renderer)) {
      const canvas = renderer.domElement
      camera.aspect = canvas.clientWidth / canvas.clientHeight
      camera.updateProjectionMatrix()
    }
})
</script>

<style lang="less" scoped>
    .scene {
        width: 100%;
        height: calc( 100vh - 76px );
        overflow: hidden;
        canvas {
            width: 100%;
            height: 100%;
        }
    }
</style>>